package com.mjk.common.util;

import com.mjk.common.base.error.BeeError;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.logger.BizLogger;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicLineParser;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

/**
 * @Auther: zyx.
 * @Date: 2018/11/19 20:03
 */ 
public class HttpUtils {

	public static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36";
	public static final String DEFAULT_ENCODING = "UTF-8";
	public static final String APPLICATION_JSON = "application/json";
	public static final String CONTENT_TYPE_TEXT_JSON = "text/json";
	private static final String RESULT_INFO = "请求地址:%s请求参数:%s返回结果:%s耗时:%s 状态码:%s";
	private static final String ERROR_INFO = "请求地址:%s 错误状态 %s";
	public static final PoolingHttpClientConnectionManager pccm ;

	
	static{
		HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> httpConnectionFactory = new ManagedHttpClientConnectionFactory(
				new DefaultHttpRequestWriterFactory(),
				new DefaultHttpResponseParserFactory(new MyLineParser(), new DefaultHttpResponseFactory()));
		pccm = new PoolingHttpClientConnectionManager(httpConnectionFactory);
		pccm.setDefaultMaxPerRoute(50);
		pccm.setMaxTotal(300);	
	}
	
	public static HttpClient buildHttpClient(Integer connectTimeout) {
		return HttpClientBuilder.create()
				.setDefaultRequestConfig(RequestConfig.custom().setConnectionRequestTimeout(15000)
						.setSocketTimeout(60000).setConnectTimeout(connectTimeout).build())
				.setRedirectStrategy(new LaxRedirectStrategy()).setConnectionManager(pccm)
				.setRetryHandler(new DefaultHttpRequestRetryHandler(1, true))
				.setUserAgent(DEFAULT_USER_AGENT).build();
	}

	/**
	 * http post请求 用于上传文件
	 * @param url ：文件上传的URL地址
	 * @param fileMap ： 文件名称和二进制文件内容的 map集合
	 * @param params ：上传参数
	 * @param headers ： 上床的http请求头
	 * @return 返回上传结果
	 */
	public static String httpPostUpFile(String url, Map<String, Object> fileMap, Map<String, Object> params,
			Map<String, String> headers) {
		return httpPostUpFile(url, fileMap, params, headers, 1000);
	}

	public static String httpPostUpFile(String url, Map<String, Object> fileMap, Map<String, Object> params,
			Map<String, String> headers, Integer timeout) {
		HttpClient httpClient = buildHttpClient(timeout);
		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setHeaders(buildHeader(headers));
			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
			builder.setCharset(StandardCharsets.UTF_8);
			builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
			if (fileMap != null && fileMap.size() > 0) {
				for (Entry<String, Object> file : fileMap.entrySet()) {
					FileBody fileBody = new FileBody((File) file.getValue(), ContentType.DEFAULT_BINARY);					
					builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
					builder.addPart(file.getKey(), fileBody);
				}
			}
			ContentType contentType = ContentType.create("text/plain",DEFAULT_ENCODING);
			for (Entry<String, Object> entry : params.entrySet()) {
				if (entry.getValue() == null)
					continue;
				builder.addTextBody(entry.getKey(), entry.getValue().toString(), contentType);
			}
			HttpEntity entity = builder.build();
			httpPost.setEntity(entity);
			HttpResponse response = httpClient.execute(httpPost);

			String result = "";
			Integer statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != null && statusCode.intValue() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING);
			} else {
				BizLogger.error(String.format(ERROR_INFO,url,response.getStatusLine().getStatusCode()));
				EntityUtils.consume(entity);
			}
			return result;
		} catch (Exception e) {
			BizLogger.error(e.getMessage());
		}
		return "";
	}

	/**
	 * 发送http post请求 并把结果写入到文件中
	 * @param url 请求地址
	 * @param params 请求参数
	 * @param headers 请求头
	 * @param filePath 返回数据的输入文件目录
	 * @return
	 */
	public static String httpPostDownFile(String url, Map<String, Object> params, Map<String, String> headers, String filePath) {
		try {
			HttpClient httpClient = buildHttpClient(40000);
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(gearUpReqHttpEntity(params, DEFAULT_ENCODING, StringEntity.class));
			httpPost.setHeaders(buildHeader(headers));
			HttpResponse response = httpClient.execute(httpPost);

			HttpEntity httpEntity = response.getEntity();
			InputStream is = httpEntity.getContent();
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int r = 0;
			while ((r = is.read(buffer)) > 0) {
				output.write(buffer, 0, r);
			}
			FileOutputStream fos = new FileOutputStream(filePath);
			output.writeTo(fos);
			output.flush();
			output.close();
			fos.close();
			EntityUtils.consume(httpEntity);
		} catch (Exception e) {
			BizLogger.error(e.getMessage());
		}
		return "";
	}

	/**
	 * 发送http请求 并返回请求结果
	 * @param url 请求的地址
	 * @param params 请求的参数 以map形式上传
	 * @param headers 请求的头
	 * @return 返回请求结果
	 */
	public static String httpPost(String url, Map<String, Object> params, Map<String, String> headers) {
		try {
			long startTime = System.currentTimeMillis();
			HttpClient httpClient = buildHttpClient(40000);
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(gearUpReqHttpEntity(params, DEFAULT_ENCODING, StringEntity.class));
			httpPost.setHeaders(buildHeader(headers));
			HttpResponse response = httpClient.execute(httpPost);
			HttpEntity httpEntity = response.getEntity();
			String result = "";
			Integer statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != null && statusCode.intValue() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING);
			} else {
				result = EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING);
				EntityUtils.consume(httpEntity);
			}
			BizLogger.info(String.format(RESULT_INFO, url, GsonUtils.bean2Json(params), result, (System.currentTimeMillis() - startTime), response.getStatusLine().getStatusCode()));
			return result;
		} catch (Exception e) {
			BizLogger.error(e.getMessage());
		}
		return "";
	}

	/**
	 * 发送http post请求 以stringentity的方式
	 * @param url 发送的请求地址
	 * @param params 发送的字符串参数
	 * @param headers 发送的请求头
	 * @return 返回请求的结果
	 */
	public static String httpPost(String url, String params, Map<String, String> headers) {
		try {
			long startTime = System.currentTimeMillis();
			HttpClient httpClient = buildHttpClient(40000);
			HttpPost httpPost = new HttpPost(url);
			StringEntity strEntity = new StringEntity(params, DEFAULT_ENCODING);
			strEntity.setContentEncoding(DEFAULT_ENCODING);
			httpPost.setEntity(strEntity);
			httpPost.setHeaders(buildHeader(headers));
			HttpResponse response = httpClient.execute(httpPost);
			HttpEntity httpEntity = response.getEntity();
			String result = "";
			Integer statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != null && statusCode.intValue() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING);
			} else {
				EntityUtils.consume(httpEntity);
			}
			BizLogger.info("请求耗时："+(System.currentTimeMillis() - startTime));
			startTime = System.currentTimeMillis();
			BizLogger.info(String.format(RESULT_INFO, url, GsonUtils.bean2Json(params), result, (System.currentTimeMillis() - startTime), response.getStatusLine().getStatusCode()));
			return result;
		} catch (Exception e) {
			BizLogger.error(e.getMessage());
		}
		return "";
	}

	/**
	 *  发送http get请求
	 * @param url 请求的URL地址
	 * @params header 请求的头信息
	 * @return 返回请求的结果
	 */
	public static String httpGet(String url) {
		return httpGet(url,null);
	}
	public static String httpGet(String url,Map<String,String>header) {
		try {
			long startTime = System.currentTimeMillis();
			HttpClient httpClient = buildHttpClient(40000);
			HttpGet get = new HttpGet(url);	
			get.setHeaders(buildHeader(header));
			HttpResponse response = httpClient.execute(get);
			HttpEntity httpEntity = response.getEntity();

			String result = "";
			Integer statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != null && statusCode.intValue() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING);
			} else {
				EntityUtils.consume(httpEntity);
			}
			BizLogger.info(String.format(RESULT_INFO, url, "", result, (System.currentTimeMillis() - startTime), response.getStatusLine().getStatusCode()));
			return result;
		} catch (Exception e) {
			BizLogger.error(e.getMessage());
		}
		return "";
	}
	/**
	 * https post请求
	 * @param url 请求的URL地址
	 * @param params 请求的参数
	 * @param headers 请求的头信息
	 * @return 返回https post的请求结果
	 */
	public static String httpsPost(String url,Map<String,Object> params,Map<String,String> headers){
		return httpsPost(url,gearUpReqHttpEntity(params),headers);
	}
	/**
	 * https请求
	 * @param url
	 * @param params
	 * @param headers
	 * @return
	 */
	public static String httpsPost(String url,String params,Map<String,String> headers){
		StringEntity strEntity = new StringEntity(params, DEFAULT_ENCODING);
		strEntity.setContentEncoding(DEFAULT_ENCODING);
		return httpsPost(url,strEntity,headers);
	}
	private static String httpsPost(String url,HttpEntity entity,Map<String,String> headers){
		try{
			RequestBuilder req = RequestBuilder.post().setUri(url);
			req.setEntity(entity);
			HttpUriRequest uri = req.build();
			if(uri == null){
				BizLogger.info(BeeError.SYSTEM_URL_IS_NULL.getMsg());
				return "";
			}
			uri.setHeaders(buildHeader(headers));
			HttpClient client = buildHttpsClient();
			if(null == client) {
				BizLogger.info(BeeError.SYSTEM_HTTPS_INIT_ERROR.getMsg());
				return null;
			}
			HttpResponse response = client.execute(uri);
			String result = "";
			Integer statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != null && statusCode.intValue() == HttpStatus.SC_OK) {
				result = EntityUtils.toString(response.getEntity(), "utf-8");
			} else {
				BizLogger.error(String.format(ERROR_INFO,url,response.getStatusLine().getStatusCode()));
				EntityUtils.consume(response.getEntity());
			}
			return result;
		}catch(Exception e){
			BizLogger.error(e);
		}
		return "";
	}

	/**
	 * map 转为 header
	 * @param params
	 * @return
	 */
	public static Header[] buildHeader(Map<String, String> params) {
		Header[] headers = null;
		if (params != null && params.size() > 0) {
			headers = new BasicHeader[params.size()];
			int i = 0;
			for (Entry<String, String> entry : params.entrySet()) {
				headers[i] = new BasicHeader(entry.getKey(), entry.getValue());
				i++;
			}
		}
		return headers;
	}

	/**
	 * 如果返回不规范的HTTP头，实现兼容.
	 */
	private static class MyLineParser extends BasicLineParser {
		@Override
		public Header parseHeader(CharArrayBuffer buffer) throws ParseException {
			try {
				return super.parseHeader(buffer);
			} catch (ParseException ex) {
				return new BasicHeader("Invalid", buffer.toString());
			}
		}
	}
	private static HttpEntity gearUpReqHttpEntity(Map<String,Object> param){
		List<String> list = new ArrayList<>();
		for (Entry<String, Object> entry : param.entrySet()) {
			String str =  entry.getKey() + "=" + entry.getValue();
			list.add(str);
		}
		HttpEntity entity = null;
		String s2 = StrUtils.join(list, "&");
		StringEntity strEntity = new StringEntity(s2, DEFAULT_ENCODING);
		strEntity.setContentType(CONTENT_TYPE_TEXT_JSON);
		strEntity.setContentEncoding(DEFAULT_ENCODING);
		entity = strEntity;
		return entity;
	}

	private static <T extends HttpEntity> HttpEntity gearUpReqHttpEntity(Map<String, Object> param, String encoding,
			Class<T> clazz) {
		List<String> list = new ArrayList<>();
		for (Entry<String, Object> entry : param.entrySet()) {
			String str = entry.getKey() + "=" + entry.getValue();
			list.add(str);
		}

		HttpEntity entity = null;
		try {
			if (clazz == UrlEncodedFormEntity.class) {
				List<NameValuePair> lx = gearUpRequestParamsToList(param);
				entity = new UrlEncodedFormEntity(lx, encoding);

			} else if (clazz == StringEntity.class) {
				String s2 = StrUtils.join(list, "&");
				StringEntity strEntity = new StringEntity(s2, encoding);
				strEntity.setContentType(APPLICATION_JSON+"; charset=UTF-8");
				strEntity.setContentEncoding(encoding);
				strEntity.setContentEncoding(encoding);
				entity = strEntity;
			}
		} catch (Exception e) {
			BizLogger.error(e);
		}
		return entity;
	}

	private static List<NameValuePair> gearUpRequestParamsToList(Map<String, Object> paramMap) {
		if (paramMap == null || paramMap.size() <= 0) {
			return Collections.emptyList();
		}

		List<NameValuePair> list = new ArrayList<>();
		try {
			Set<Entry<String, Object>> set = paramMap.entrySet();
			for (Entry<String, Object> entry : set) {
				String key = entry.getKey();
				if (entry.getValue() != null) {
					String value = entry.getValue().toString();
					NameValuePair nvp = new BasicNameValuePair(key, value);
					list.add(nvp);
				}
			}
		} catch (Exception e) {
			BizLogger.error(e);
		}
		return list;
	}
	protected static HttpClient buildHttpsClient() {
		SSLContext sslcontext = null;
		try {
			sslcontext = createIgnoreVerifySSL();
		}catch (Exception e){
			sslcontext = null;
		}
		if(null == sslcontext){
			return null;
		}
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("https", new SSLConnectionSocketFactory(sslcontext,NoopHostnameVerifier.INSTANCE))
				.build();
		try(PoolingHttpClientConnectionManager connManager =new PoolingHttpClientConnectionManager(socketFactoryRegistry);){

//			SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(
//					 sslcontext, NoopHostnameVerifier.INSTANCE);
//	        CloseableHttpClient httpclient = HttpClients.custom()
//	                    .setSSLSocketFactory(ssf).build();
//	        return httpclient;
			// 设置协议http和https对应的处理socket链接工厂的对象  

			
			return HttpClientBuilder.create().setDefaultRequestConfig(getConfig())
					.setRedirectStrategy(new LaxRedirectStrategy())
					.setConnectionManager(connManager)
					.setRetryHandler(new DefaultHttpRequestRetryHandler(1, true))
					.setUserAgent(DEFAULT_USER_AGENT).build();
		}catch(Exception e){
			BizLogger.error(e);
		}
		return null;
	}
	protected static RequestConfig getConfig() {
		return RequestConfig.custom()
				.setConnectionRequestTimeout(15000)
				.setSocketTimeout(6000)
				.setConnectTimeout(10000).build();
	}
	/** 
	 * 绕过验证 
	 *   
	 * @return 
	 * @throws NoSuchAlgorithmException  
	 * @throws KeyManagementException  
	 */  
	private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("TLSv1.2");
		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法  
		X509TrustManager trustManager = new X509TrustManager() {  
			public void checkClientTrusted(  
					X509Certificate[] paramArrayOfX509Certificate,
					String paramString) {
				// Do nothing
			}   
			public void checkServerTrusted(  
					X509Certificate[] paramArrayOfX509Certificate,
					String paramString) {
				// Do nothing
			}   
			public X509Certificate[] getAcceptedIssuers() {
				return new X509Certificate[0];
			}  
		};  

		sc.init(null, new TrustManager[] { trustManager }, null);  
		return sc;  
	}


}
